package com.yz.fanrenmianshi.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yz.fanrenmianshi.constant.CommentThumbRedisConstant;
import com.yz.fanrenmianshi.mapper.QuestionCommentMapper;
import com.yz.fanrenmianshi.mapper.QuestionCommentThumbMapper;
import com.yz.fanrenmianshi.model.entity.QuestionComment;
import com.yz.fanrenmianshi.model.entity.QuestionCommentThumb;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 评论点赞Redis服务类
 * 负责处理评论点赞相关的Redis操作，提供高性能的点赞功能
 */
@Slf4j
@Service
public class CommentThumbRedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private QuestionCommentThumbMapper questionCommentThumbMapper;

    @Resource
    private QuestionCommentMapper questionCommentMapper;

    /**
     * 点赞评论
     * 使用Redis事务保证原子性
     *
     * @param commentId 评论ID
     * @param userId 用户ID
     * @return true-点赞成功，false-已经点过赞
     */
    public boolean likeComment(Long commentId, Long userId) {
        try {
            String likeSetKey = CommentThumbRedisConstant.getCommentLikeSetKey(commentId);
            String countKey = CommentThumbRedisConstant.getCommentLikeCountKey(commentId);
            String userLikeSetKey = CommentThumbRedisConstant.getUserLikeCommentSetKey(userId);

            // 使用Lua脚本保证原子性
            String luaScript = 
                "local likeSetKey = KEYS[1] " +
                "local countKey = KEYS[2] " +
                "local userLikeSetKey = KEYS[3] " +
                "local userId = ARGV[1] " +
                "local commentId = ARGV[2] " +
                "\n" +
                "-- 检查用户是否已经点赞\n" +
                "local exists = redis.call('SISMEMBER', likeSetKey, userId) " +
                "if exists == 1 then " +
                "    return 0 " + // 已经点过赞
                "end " +
                "\n" +
                "-- 添加到点赞集合\n" +
                "redis.call('SADD', likeSetKey, userId) " +
                "-- 检查并初始化点赞数量（如果不存在则设置为0）\n" +
                "if redis.call('EXISTS', countKey) == 0 then " +
                "    redis.call('SET', countKey, 0) " +
                "end\n" +
                "-- 增加点赞数量\n" +
                "redis.call('INCR', countKey) " +
                "-- 添加到用户点赞集合\n" +
                "redis.call('SADD', userLikeSetKey, commentId) " +
                "\n" +
                "-- 设置过期时间\n" +
                "redis.call('EXPIRE', likeSetKey, ARGV[3]) " +
                "redis.call('EXPIRE', countKey, ARGV[4]) " +
                "redis.call('EXPIRE', userLikeSetKey, ARGV[3]) " +
                "\n" +
                "return 1"; // 点赞成功

            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(luaScript);
            redisScript.setResultType(Long.class);

            // 使用StringRedisTemplate执行Lua脚本，避免Jackson序列化问题
            Long result = stringRedisTemplate.execute(
                redisScript,
                Arrays.asList(likeSetKey, countKey, userLikeSetKey),
                userId.toString(),
                commentId.toString(),
                String.valueOf(CommentThumbRedisConstant.COMMENT_THUMB_DATA_EXPIRE_SECONDS),
                String.valueOf(CommentThumbRedisConstant.COMMENT_THUMB_COUNT_EXPIRE_SECONDS)
            );

            if (result == 1) {
                log.info("用户 {} 点赞评论 {} 成功", userId, commentId);
                // 记录需要同步的评论
                stringRedisTemplate.opsForSet().add("comment:thumb:sync:set", commentId.toString());
                stringRedisTemplate.expire("comment:thumb:sync:set", 86400, TimeUnit.SECONDS);
                return true;
            } else {
                log.warn("用户 {} 已经点赞过评论 {}", userId, commentId);
                return false;
            }

        } catch (Exception e) {
            log.error("点赞评论失败，commentId: {}, userId: {}", commentId, userId, e);
            throw new RuntimeException("点赞操作失败", e);
        }
    }

    /**
     * 取消点赞评论
     * 使用Redis事务保证原子性
     *
     * @param commentId 评论ID
     * @param userId 用户ID
     * @return true-取消点赞成功，false-未点过赞
     */
    public boolean unlikeComment(Long commentId, Long userId) {
        try {
            String likeSetKey = CommentThumbRedisConstant.getCommentLikeSetKey(commentId);
            String countKey = CommentThumbRedisConstant.getCommentLikeCountKey(commentId);
            String userLikeSetKey = CommentThumbRedisConstant.getUserLikeCommentSetKey(userId);

            // 使用Lua脚本保证原子性
            String luaScript = 
                "local likeSetKey = KEYS[1] " +
                "local countKey = KEYS[2] " +
                "local userLikeSetKey = KEYS[3] " +
                "local userId = ARGV[1] " +
                "local commentId = ARGV[2] " +
                "\n" +
                "-- 检查用户是否已经点赞\n" +
                "local exists = redis.call('SISMEMBER', likeSetKey, userId) " +
                "if exists == 0 then " +
                "    return 0 " + // 未点过赞
                "end " +
                "\n" +
                "-- 从点赞集合移除\n" +
                "redis.call('SREM', likeSetKey, userId) " +
                "-- 检查点赞数量是否存在，如果存在则减少\n" +
                "if redis.call('EXISTS', countKey) == 1 then " +
                "    redis.call('DECR', countKey) " +
                "end " +
                "-- 从用户点赞集合移除\n" +
                "redis.call('SREM', userLikeSetKey, commentId) " +
                "\n" +
                "return 1"; // 取消点赞成功

            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(luaScript);
            redisScript.setResultType(Long.class);

            // 使用StringRedisTemplate执行Lua脚本，避免Jackson序列化问题
            Long result = stringRedisTemplate.execute(
                redisScript,
                Arrays.asList(likeSetKey, countKey, userLikeSetKey),
                userId.toString(),
                commentId.toString()
            );

            if (result != null && result == 1) {
                log.info("用户 {} 取消点赞评论 {} 成功", userId, commentId);
                // 记录需要同步的评论
                stringRedisTemplate.opsForSet().add("comment:thumb:sync:set", commentId.toString());
                stringRedisTemplate.expire("comment:thumb:sync:set", 86400, TimeUnit.SECONDS);
                return true;
            } else {
                log.warn("用户 {} 未点赞过评论 {}，无法取消", userId, commentId);
                return false;
            }

        } catch (Exception e) {
            log.error("取消点赞评论失败，commentId: {}, userId: {}", commentId, userId, e);
            throw new RuntimeException("取消点赞操作失败", e);
        }
    }

    /**
     * 检查用户是否点赞了评论
     *
     * @param commentId 评论ID
     * @param userId 用户ID
     * @return true-已点赞，false-未点赞
     */
    public boolean hasUserLikedComment(Long commentId, Long userId) {
        try {
            String likeSetKey = CommentThumbRedisConstant.getCommentLikeSetKey(commentId);
            // 使用StringRedisTemplate避免序列化问题，确保与点赞/取消点赞操作一致
            Boolean result = stringRedisTemplate.opsForSet().isMember(likeSetKey, userId.toString());
            return result != null && result;
        } catch (Exception e) {
            log.error("检查用户点赞状态失败，commentId: {}, userId: {}", commentId, userId, e);
            // 如果Redis查询失败，降级到数据库查询
            return checkUserLikedFromDb(commentId, userId);
        }
    }

    /**
     * 获取评论的点赞数量
     *
     * @param commentId 评论ID
     * @return 点赞数量
     */
    public Long getCommentLikeCount(Long commentId) {
        try {
            String countKey = CommentThumbRedisConstant.getCommentLikeCountKey(commentId);
            
            // 使用StringRedisTemplate获取值，避免Jackson序列化问题
            String countStr = stringRedisTemplate.opsForValue().get(countKey);
            
            if (countStr != null) {
                try {
                    return Long.parseLong(countStr);
                } catch (NumberFormatException e) {
                    log.warn("点赞数量格式错误，commentId: {}, countStr: {}", commentId, countStr);
                    // 如果格式错误，删除该键并重新初始化
                    stringRedisTemplate.delete(countKey);
                }
            }
            
            // 如果Redis中没有数据，从数据库获取并缓存
            Long count = getCommentLikeCountFromDb(commentId);
            if (count != null && count > 0) {
                // 使用INCR命令确保点赞数量以整数形式存储
                stringRedisTemplate.opsForValue().increment(countKey, count);
            } else if (count != null) {
                // 如果数量为0，使用SET初始化为0并设置过期时间
                stringRedisTemplate.opsForValue().set(countKey, "0");
            }
            
            // 设置过期时间
            stringRedisTemplate.expire(countKey, CommentThumbRedisConstant.COMMENT_THUMB_COUNT_EXPIRE_SECONDS, TimeUnit.SECONDS);
            
            return count != null ? count : 0L;
            
        } catch (Exception e) {
            log.error("获取评论点赞数量失败，commentId: {}", commentId, e);
            // 如果Redis查询失败，降级到数据库查询
            return getCommentLikeCountFromDb(commentId);
        }
    }

    /**
     * 批量获取评论的点赞数量
     *
     * @param commentIds 评论ID列表
     * @return 点赞数量数组
     */
    public Long[] getCommentLikeCounts(Long[] commentIds) {
        Long[] counts = new Long[commentIds.length];
        for (int i = 0; i < commentIds.length; i++) {
            counts[i] = getCommentLikeCount(commentIds[i]);
        }
        return counts;
    }

    /**
     * 批量检查用户是否点赞了评论
     *
     * @param commentIds 评论ID数组
     * @param userId 用户ID
     * @return 点赞状态数组
     */
    public Boolean[] hasUserLikedComments(Long[] commentIds, Long userId) {
        Boolean[] results = new Boolean[commentIds.length];
        for (int i = 0; i < commentIds.length; i++) {
            results[i] = hasUserLikedComment(commentIds[i], userId);
        }
        return results;
    }

    /**
     * 从数据库检查用户是否点赞了评论（降级方案）
     *
     * @param commentId 评论ID
     * @param userId 用户ID
     * @return true-已点赞，false-未点赞
     */
    private boolean checkUserLikedFromDb(Long commentId, Long userId) {
        try {
            QueryWrapper<QuestionCommentThumb> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("commentId", commentId).eq("userId", userId);
            return questionCommentThumbMapper.selectCount(queryWrapper) > 0;
        } catch (Exception e) {
            log.error("数据库查询用户点赞状态失败，commentId: {}, userId: {}", commentId, userId, e);
            return false;
        }
    }

    /**
     * 从数据库获取评论点赞数量（降级方案）
     *
     * @param commentId 评论ID
     * @return 点赞数量
     */
    private Long getCommentLikeCountFromDb(Long commentId) {
        try {
            QueryWrapper<QuestionCommentThumb> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("commentId", commentId);
            return (long) questionCommentThumbMapper.selectCount(queryWrapper);
        } catch (Exception e) {
            log.error("数据库查询评论点赞数量失败，commentId: {}", commentId, e);
            return 0L;
        }
    }

    /**
     * 同步评论点赞数据到数据库
     * 用于异步持久化Redis中的点赞数据
     *
     * @param commentId 评论ID
     * @return true-同步成功，false-同步失败
     */
    public boolean syncCommentThumbToDb(Long commentId) {
        try {
            String likeSetKey = CommentThumbRedisConstant.getCommentLikeSetKey(commentId);
            String countKey = CommentThumbRedisConstant.getCommentLikeCountKey(commentId);

            // 获取Redis中的点赞用户集合
            Long redisCount = stringRedisTemplate.opsForSet().size(likeSetKey);
            if (redisCount == null) {
                return true; // Redis中没有数据，无需同步
            }

            // 获取Redis中的点赞数量（使用StringRedisTemplate获取）
            String countStr = stringRedisTemplate.opsForValue().get(countKey);
            Long redisLikeCount = 0L;
            if (countStr != null) {
                try {
                    redisLikeCount = Long.parseLong(countStr);
                } catch (NumberFormatException e) {
                    log.warn("点赞数量格式错误，commentId: {}, countStr: {}", commentId, countStr);
                    redisLikeCount = 0L;
                }
            }

            // 更新数据库中的点赞数量
            QuestionComment comment = questionCommentMapper.selectById(commentId);
            if (comment != null) {
                comment.setThumbNum(redisLikeCount.intValue());
                questionCommentMapper.updateById(comment);
                log.info("同步评论点赞数量到数据库成功，commentId: {}, thumbNum: {}", commentId, redisLikeCount);
            }

            return true;
        } catch (Exception e) {
            log.error("同步评论点赞数据到数据库失败，commentId: {}", commentId, e);
            return false;
        }
    }

    /**
     * 获取需要同步的评论ID列表
     * @return 评论ID集合
     */
    public Set<String> getCommentIdsNeedSync() {
        Set<String> result = stringRedisTemplate.opsForSet().members("comment:thumb:sync:set");
        if (result == null) {
            return new java.util.HashSet<>();
        }
        return result;
    }

    /**
     * 获取评论的点赞用户列表
     * @param commentId 评论ID
     * @return 点赞用户ID集合
     */
    public Set<String> getCommentLikedUsers(Long commentId) {
        Set<String> result = stringRedisTemplate.opsForSet().members(CommentThumbRedisConstant.getCommentLikeSetKey(commentId));
        if (result == null) {
            return new java.util.HashSet<>();
        }
        return result;
    }

    /**
     * 重新初始化评论点赞数量（用于修复数据类型问题）
     * @param commentId 评论ID
     */
    public void reinitializeCommentLikeCount(Long commentId) {
        try {
            String countKey = CommentThumbRedisConstant.getCommentLikeCountKey(commentId);
            
            // 删除现有的点赞数量键（无论是字符串还是整数格式）
            stringRedisTemplate.delete(countKey);
            
            // 从数据库获取正确的点赞数量
            Long count = getCommentLikeCountFromDb(commentId);
            if (count != null && count > 0) {
                // 使用INCR命令初始化点赞数量为整数格式
                stringRedisTemplate.opsForValue().increment(countKey, count);
                // 设置过期时间
                stringRedisTemplate.expire(countKey, CommentThumbRedisConstant.COMMENT_THUMB_COUNT_EXPIRE_SECONDS, TimeUnit.SECONDS);
                log.info("重新初始化评论点赞数量成功，commentId: {}, count: {}", commentId, count);
            } else {
                // 如果数据库中没有数据，直接设置为0
                stringRedisTemplate.opsForValue().set(countKey, "0");
                stringRedisTemplate.expire(countKey, CommentThumbRedisConstant.COMMENT_THUMB_COUNT_EXPIRE_SECONDS, TimeUnit.SECONDS);
                log.info("评论点赞数量初始化为0，commentId: {}", commentId);
            }
        } catch (Exception e) {
            log.error("重新初始化评论点赞数量失败，commentId: {}", commentId, e);
        }
    }

    /**
     * 检查用户点赞频率是否超限
     * 基于Redis计数器实现，1秒内超过5次则警告
     *
     * @param userId 用户ID
     * @return true-频率正常，false-频率过高
     */
    public boolean checkThumbRateLimit(Long userId) {
        try {
            String rateLimitKey = CommentThumbRedisConstant.getThumbRateLimitKey(userId);
            
            // 使用Redis的INCR命令实现计数器
            Long currentCount = stringRedisTemplate.opsForValue().increment(rateLimitKey, 1);
            
            if (currentCount == null) {
                log.error("Redis计数器操作失败，userId: {}", userId);
                return true; // 降级处理，允许操作
            }
            
            // 如果是第一次访问，设置过期时间为1秒
            if (currentCount == 1) {
                stringRedisTemplate.expire(rateLimitKey, 
                    CommentThumbRedisConstant.THUMB_RATE_LIMIT_WINDOW_SECONDS, 
                    TimeUnit.SECONDS);
            }
            
            // 检查是否超过阈值（5次/秒）
            if (currentCount > CommentThumbRedisConstant.THUMB_RATE_LIMIT_THRESHOLD) {
                log.warn("用户 {} 点赞频率过高，1秒内已达 {} 次", userId, currentCount);
                return false;
            }
            
            return true;
            
        } catch (Exception e) {
            log.error("检查点赞频率失败，userId: {}", userId, e);
            return true; // 降级处理，允许操作
        }
    }
}